package net.gdface.bean;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;

import net.gdface.bean.exception.BeanPropertyRuntimeException;
import net.gdface.bean.exception.CycleReferenceException;
import net.gdface.bean.exception.NestedNullException;
import net.gdface.reflection.MethodUtils;
import static java.util.Objects.deepEquals;
import static net.gdface.json.JsonSupports.jsonSupportInstance;
import static net.gdface.utils.CaseSupport.toNestedCamelcase;
import static net.gdface.utils.CaseSupport.TO_NESTED_CAMEL_CASE;

/**
 * 参照 common-beanutils org.apache.commons.beanutils.PropertyUtilsBean 实现 Java Bean 的多级嵌套读写，
 * 扩展支持Map,类成员，JSON String(需要JSON库[fastjson or jackson]支持) ,并提供自定义 PropertyDescriptor 支持
 * @author guyadong
 * @since 3.2.0
 */
public abstract class BaseBeanPropertySupport {
	private final NameResolver resolver = new NameResolver();
	
	/** An empty object array */
	protected static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

	protected BaseBeanPropertySupport() {
	}

	/**
	 * 获取beanClass中所有具有指定读写类型(rw)的属性名集合
	 * @param beanClass
	 * @param rw 属性类型标记 <br>
	 * 					<li>0 所有属性</li>
	 * 					<li>1 读属性</li>
	 * 					<li>2 写属性</li>
	 * 					<li>3 读写属性</li>
	 * @param lenient 是否为宽容模式---允许返回类型不为void的setter方法
	 * @return 属性名与PropertyDescriptor映射的Map对象
	 * @since 2.8.0
	 */
	public abstract Set<String> getPropertyNames(Class<?> beanClass,int rw, boolean lenient);
	/**
	 * @see #getPropertyNames(Class, int, boolean)
	 * @since 2.8.0
	 */
	public Set<String> getPropertyNames(Object bean,int rw, boolean lenient){
		return null == bean ? Collections.<String>emptySet() : getPropertyNames(bean.getClass(),rw, lenient);
	}
	/**
     * Clear any cached property descriptors information for all classes
     * loaded by any class loaders.  This is useful in cases where class
     * loaders are thrown away to implement class reloading.
     */
    public abstract void clearDescriptors();
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     * 
     * @see #getProperty(Object, String, Object)
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @return the nested property value
     * 
     */
    public Object getProperty(Object bean, String name){
    	return getProperty(bean,name,null);
    }
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @param defaultValue default value if value of property is null
     * @return the nested property value
     * @throws BeanPropertyRuntimeException wrap all reflect option exception 
     * 
     */
    public Object getProperty(Object bean, String name,Object defaultValue)
    		throws BeanPropertyRuntimeException {
    	try {
    		Object value = getPropertyChecked(bean,name);
    		if(null == value){
    			return defaultValue;
    		}
    		if(null != defaultValue && !defaultValue.getClass().isInstance(value) && maybeJsonString(value) > 0){
    			/** 尝试解析为对象,解析失败则忽略返回原始值 */
   				return jsonSupportInstance().parseOrEmptyMap((String)value,false);
    		}
    		return value;
    	} catch (NoSuchMethodException e) {
    		return defaultValue;
    	}catch (ReflectiveOperationException e) {
    		throw new BeanPropertyRuntimeException(e);
    	}catch (IndexOutOfBoundsException e) {
    		return defaultValue;
		}
    }
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, cast to target type.if value is String,try to parse json to target type.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @param targetType type required
     * @return the nested property value
     * @throws BeanPropertyRuntimeException wrap all reflect option exception 
     * 
     */
    public <T>T getPropertyCheckType(Object bean, String name,Class<T> targetType)
    		throws BeanPropertyRuntimeException {
    	try {
    		Object value = getPropertyChecked(bean,name);
    		if(null == value){
    			return null;
    		}
    		if(null == targetType){
    			throw new NullPointerException("targetType is null");
    		}
    		try {
    			return targetType.cast(value);
			} catch (ClassCastException e) {
				if(value instanceof String){
					/** 尝试解析为对象 */
					return jsonSupportInstance().parse((String)value,targetType);
				}
				throw e;
			}
    	} catch (NoSuchMethodException e) {
    		return null;
    	}catch (ReflectiveOperationException e) {
    		throw new BeanPropertyRuntimeException(e);
    	}
    }
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @return the nested property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception NestedNullException if a nested reference to a
     *  property returns null
     * @exception InvocationTargetException
     * if the property accessor method throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    public Object getPropertyChecked(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        return new NestedContext(bean,name, false).visit(new ReadVisitor());

    }

    protected Object getProperty0(Object bean, String name) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
        if (resolver.isMapped(name)) {
            bean = getMappedProperty(bean, name);
        } else if (resolver.isIndexed(name)) {
            bean = getIndexedProperty(bean, name);
        } else if (resolver.isSearched(name)) {
            bean = getSearchedProperty(bean, name);
        }else {
            bean = getSimpleProperty(bean, name);
        }
        return bean;
    }
    /**
     * Return the value of the specified mapped property of the
     * specified bean, with no type conversions.  The key of the
     * required value must be included (in brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.
     *
     * @param bean Bean whose property is to be extracted
     * @param name <code>propertyname(key)</code> of the property value
     *  to be extracted
     * @return the mapped property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getMappedProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the key of the requested individual property
        String key  = null;
        try {
            key = resolver.getKey(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException
                    ("Invalid mapped property '" + name +
                    "' on bean class '" + bean.getClass() + "' " + e.getMessage());
        }
        if (key == null) {
            throw new IllegalArgumentException("Invalid mapped property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        return (getMappedProperty(bean, name, key));

    }
    /**
     * Return the value of the specified mapped property of the specified
     * bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Mapped property name of the property value to be extracted
     * @param key Key of the property value to be extracted
     * @return the mapped property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected abstract Object getMappedProperty(Object bean,
                                           String name, String key)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException ;
    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  The zero-relative index of the
     * required value must be included (in square brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.  In addition to supporting the JavaBeans specification, this
     * method has been extended to support <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name <code>propertyname[index]</code> of the property value
     *  to be extracted
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying array or List
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getIndexedProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the index of the requested individual property
        int index;
        try {
            index = resolver.getIndex(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "' " +
                    e.getMessage());
        }
        if (index < -2) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        return (getIndexedProperty(bean, name, index));

    }


    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  In addition to supporting the JavaBeans
     * specification, this method has been extended to support
     * <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Simple property name of the property value to be extracted
     * @param index Index of the property value to be extracted
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected abstract Object getIndexedProperty(Object bean,
                                            String name, int index)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException ;
    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  The zero-relative index of the
     * required value must be included (in square brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.  In addition to supporting the JavaBeans specification, this
     * method has been extended to support <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name <code>propertyname[index]</code> of the property value
     *  to be extracted
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying array or List
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getSearchedProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the index of the requested individual property
        String field = null;
        String conditionValue = null;
        try {
            field = resolver.getField(name);
            conditionValue = resolver.getValue(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "' " +
                    e.getMessage());
        }
        if (null == field || 0 == field.length() || null == conditionValue || 0 == conditionValue.length()) {
            throw new IllegalArgumentException("Invalid search property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        return (getSearchedProperty(bean, name, field,conditionValue));

    }
    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  In addition to supporting the JavaBeans
     * specification, this method has been extended to support
     * <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Simple property name of the property value to be extracted
     * @param field sub property name
     * @param conditionValue search satisfied value of field  
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  property cannot be found
     */
    protected abstract Object getSearchedProperty(Object bean,
    		String name,String field,Object conditionValue)
    				throws IllegalAccessException, InvocationTargetException,
    				NoSuchMethodException ;
    /**
     * Return the value of the specified simple property of the specified
     * bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Name of the property to be extracted
     * @return The property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception IllegalArgumentException if the property name
     *  is nested or indexed
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected abstract Object getSimpleProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException;
    /**
     * Set the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     * @see #setPropertyChecked(Object, String, Object)
     * @param bean Bean whose property is to be modified
     * @param name Possibly nested name of the property to be modified
     * @param value Value to which the property is to be set
     * @return bean
     *  @throws BeanPropertyRuntimeException wrap all reflect option exception
     */
    public Object setProperty(Object bean,String name, Object value){
    	try {
			return setPropertyChecked(bean,name,value);
		} catch (NoSuchMethodException e) {
			// DO NOTHING
			return bean;
		}catch (ReflectiveOperationException e) {
			throw new BeanPropertyRuntimeException(e);
		}
    }
    
    /**
     * Set the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     * <p>
     * Example values for parameter "name" are:
     * <ul>
     * <li> "a" -- sets the value of property a of the specified bean </li>
     * <li> "a.b" -- gets the value of property a of the specified bean,
     * then on that object sets the value of property b.</li>
     * <li> "a(key)" -- sets a value of mapped-property a on the specified
     * bean. This effectively means bean.setA("key").</li>
     * <li> "a[3]" -- sets a value of indexed-property a on the specified
     * bean. This effectively means bean.setA(3).</li>
     * </ul>
     *
     * @param bean Bean whose property is to be modified
     * @param name Possibly nested name of the property to be modified
     * @param value Value to which the property is to be set
     * @return bean
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception IllegalArgumentException if a nested reference to a
     *  property returns null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    public Object setPropertyChecked(Object bean,
                                         String name, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
    	return new NestedContext(bean, name, value).visit(new WriteVisitor());
    }
    protected Object setProperty0(Object bean,
            String name, Object value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{

    	if (resolver.isMapped(name)) {
    		setMappedProperty(bean, name, value);
    	} else if (resolver.isIndexed(name)) {
    		setIndexedProperty(bean, name, value);
    	} else {
    		setSimpleProperty(bean, name, value);
    	}
    	return bean;
    }
    
    /**
     * Set the value of the specified mapped property of the
     * specified bean, with no type conversions.  The key of the
     * value to set must be included (in brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.
     *
     * @param bean Bean whose property is to be set
     * @param name <code>propertyname(key)</code> of the property value
     *  to be set
     * @param value The property value to be set
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected void setMappedProperty(Object bean, String name,
                                         Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the key of the requested individual property
        String key  = null;
        try {
            key = resolver.getKey(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException
                    ("Invalid mapped property '" + name +
                    "' on bean class '" + bean.getClass() + "'");
        }
        if (key == null) {
            throw new IllegalArgumentException
                    ("Invalid mapped property '" + name +
                    "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        setMappedProperty(bean, name, key, value);

    }


    /**
     * Set the value of the specified mapped property of the specified
     * bean, with no type conversions.
     *
     * @param bean Bean whose property is to be set
     * @param name Mapped property name of the property value to be set
     * @param key Key of the property value to be set
     * @param value The property value to be set
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected abstract void setMappedProperty(Object bean, String name,
                                         String key, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException ;
    /**
     * Set the value of the specified indexed property of the specified
     * bean, with no type conversions.  The zero-relative index of the
     * required value must be included (in square brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.  In addition to supporting the JavaBeans specification, this
     * method has been extended to support <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be modified
     * @param name <code>propertyname[index]</code> of the property value
     *  to be modified
     * @param value Value to which the specified property element
     *  should be set
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected void setIndexedProperty(Object bean, String name,
                                          Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the index of the requested individual property
        int index;
        try {
            index = resolver.getIndex(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }
        if (index < -2) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);        
        // Set the specified indexed property value
        setIndexedProperty(bean, name, index, value);

    }

    /**
     * Set the value of the specified indexed property of the specified
     * bean, with no type conversions.  In addition to supporting the JavaBeans
     * specification, this method has been extended to support
     * <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be set
     * @param name Simple property name of the property value to be set
     * @param index Index of the property value to be set
     * @param value Value to which the indexed property element is to be set
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
	protected abstract void setIndexedProperty(Object bean, String name,
                                          int index, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException ;
    /**
	 * 设置{@link List}对象索引指定位置的值<br>
	 * index为-1时向列表头部添加元素,
	 * index为-2时向列表尾部添加元素,
	 * @param name 
	 * @param bean
	 * @param index
	 * @param value
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected void setOrAddIndexValue(String name,Object bean,int index, Object value){
		if(null == bean){
	    	throw new IllegalArgumentException("Property '" + name + "' is null ");
		}else if(bean.getClass().isArray()){
			switch (index) {
			case -1:
				Array.set(bean, 0, value);
				break;
			default:
				Array.set(bean, index, value);
				break;
			}
		}else if(bean instanceof List){
			List list = (List)bean;
			switch (index) {
			case -1:
				/** 添加到列表头部 */
				list.add(0, value);
				break;
			case -2:
				/** 添加到列表尾部 */
				list.add(value);
				break;
			default:
				list.set(index, value);
				break;
			}	
		}else{
			throw new IllegalArgumentException("Property '" + name +
					"' is not indexed on bean class '" + bean.getClass() + "'");
		}
	}

	/**
	 * 返回{@link List}对象索引指定位置的值<br>
	 * index为-1时返回列表第一个元素，等同于0,
	 * index为-2时返回列表最后一个元素,
	 * @param name
	 * @param bean
	 * @param index
	 */
	protected Object getIndexValue(String name,Object bean,int index){
		if(null == bean){
	    	return null;
		}else if(bean.getClass().isArray()){
			try {
				switch (index) {
				case -1:
					/** 添加到列表头部 */
					return Array.get(bean,0);
				case -2:
					/** 返回列表最后一个元素 */
					return Array.get(bean,Array.getLength(bean)-1);
				default:
					return Array.get(bean,index);
				}	    		
				
			} catch (ArrayIndexOutOfBoundsException e) {
	            throw new ArrayIndexOutOfBoundsException("Index: " +
	                    index + ", Size: " + Array.getLength(bean) +
	                    " for property '" + name + "'");
	        }
			
		}else if(bean instanceof List){
			List<?> list = (List<?>)bean;
			switch (index) {
			case -1:
				/** 添加到列表头部 */
				return list.get(0);
			case -2:
				/** 返回列表最后一个元素 */
				return list.get(list.size()-1);
			default:
				return list.get(index);
			}	
		}else {
			throw new IllegalArgumentException("Property '" + name +
	                "' is not indexed on bean class '" + bean.getClass() + "'");
		}
	}

	/**
     * Set the value of the specified simple property of the specified bean,
     * with no type conversions.
     *
     * @param bean Bean whose property is to be modified
     * @param name Name of the property to be modified
     * @param value Value to which the property should be set
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception IllegalArgumentException if the property name is
     *  nested or indexed
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected abstract void setSimpleProperty(Object bean,
                                         String name, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException ;

	/**
     * <p>Copy property values from the "origin" bean to the "destination" bean
     * for all cases where the property names are the same (even though the
     * actual getter and setter methods might have been customized via
     * <code>BeanInfo</code> classes).  No conversions are performed on the
     * actual property values -- it is assumed that the values retrieved from
     * the origin bean are assignment-compatible with the types expected by
     * the destination bean.</p>
     *
     * <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
     * to contain String-valued <strong>simple</strong> property names as the keys, pointing
     * at the corresponding property values that will be set in the destination
     * bean.<strong>Note</strong> that this method is intended to perform
     * a "shallow copy" of the properties and so complex properties
     * (for example, nested ones) will not be copied.</p>
     *
     * <p>Note, that this method will not copy a List to a List, or an Object[]
     * to an Object[]. It's specifically for copying JavaBean properties. </p>
     *
     * @param dest Destination bean whose properties are modified
     * @param orig Origin bean whose properties are retrieved
     *  <code>orig</code> argument is null
	 * @param ignoreNull Ignore fields as {@ code null} for {@ code true}
	 * @param ignoreEmpty Ignore empty String type fields or Collections for true, Iterable,Iterator,Map, Array type
	 * @since 3.2.1
     */
    public abstract void copyProperties(Object dest, Object orig, boolean ignoreNull, boolean ignoreEmpty) ;
    /**
     * <p>Copy property values from the "origin" bean to the "destination" bean
     * for all cases where the property names are the same (even though the
     * actual getter and setter methods might have been customized via
     * <code>BeanInfo</code> classes).  No conversions are performed on the
     * actual property values -- it is assumed that the values retrieved from
     * the origin bean are assignment-compatible with the types expected by
     * the destination bean.</p>
     *
     * <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
     * to contain String-valued <strong>simple</strong> property names as the keys, pointing
     * at the corresponding property values that will be set in the destination
     * bean.<strong>Note</strong> that this method is intended to perform
     * a "shallow copy" of the properties and so complex properties
     * (for example, nested ones) will not be copied.</p>
     *
     * <p>Note, that this method will not copy a List to a List, or an Object[]
     * to an Object[]. It's specifically for copying JavaBean properties. </p>
     *
     * @param dest Destination bean whose properties are modified
     * @param orig Origin bean whose properties are retrieved
     *  <code>orig</code> argument is null
     */
    public abstract void copyProperties(Object dest, Object orig) ;

	/**
	 * <p>Return <code>true</code> if the specified property name identifies
	 * a readable property on the specified bean; otherwise, return
	 * <code>false</code>.
	 *
	 * @param bean Bean to be examined
	 * @param name Property name to be evaluated
	 * @return <code>true</code> if the property is readable,
	 * otherwise <code>false</code>
	 *
	 * @exception IllegalArgumentException if <code>bean</code>
	 *  or <code>name</code> is <code>null</code>
	 *
	 */
	public abstract boolean isReadable(Object bean, String name) ;

	/**
	 * <p>Return <code>true</code> if the specified property name identifies
	 * a writeable property on the specified bean; otherwise, return
	 * <code>false</code>.
	 *
	 * @param bean Bean to be examined
	 * @param name Property name to be evaluated
	 * @return <code>true</code> if the property is writeable,
	 * otherwise <code>false</code>
	 *
	 * @exception IllegalArgumentException if <code>bean</code>
	 *  or <code>name</code> is <code>null</code>
	 *
	 */
	public abstract boolean isWriteable(Object bean, String name);

	public <T>BeanDifference createBeanDifference(final Function<T,Object> rightGetter){
		if(null == rightGetter){
			throw new NullPointerException("rightGetter is null");
		}
		return new BeanDifference(this) {
			@SuppressWarnings("unchecked")
			@Override
			protected Object left(Object right) {
				return rightGetter.apply((T) right);
			}
		};
	}
    /**
     * 以嵌套节点方式输出指定对象所有字段内容
     * @param bean Bean whose properties are to be extracted
     * @return The set of properties for the bean
     */
    public Map<String, Object> describe(Object bean){
    	return describe(bean,false);
    }
    /**
     * 以嵌套节点方式输出指定对象所有字段内容
     * @param bean Bean whose properties are to be extracted
     * @param throwOnCycleReference 当检测到循环引用时是否抛出{@link CycleReferenceException}
     * @return The set of properties for the bean
     * @since 2.7.7
     */
    public Map<String, Object> describe(Object bean,boolean throwOnCycleReference){
    	Map<String, Object> nodes = new LinkedHashMap<String, Object>();
    	NestedContext nestedContext = new NestedContext(bean, null, throwOnCycleReference);
    	nestedContext.describe(nodes);
    	return nodes;
    }
    /**
     * 检查是否有循环引用,如果有循环引用返回{@code true}否则返回{@code false}
     * @param bean
     * @since 2.7.7
     */
    public boolean hasCycleReference(Object bean) {
    	try {
    		checkCycleReference(bean);
			return false;
		} catch (CycleReferenceException e) {
			return true;
		}
    }
    /**
     * 检查是否有循环引用,有则抛出异常
     * @param bean
     * @throws CycleReferenceException 检测到循环引用
     * @since 2.7.7
     */
    public void checkCycleReference(Object bean) throws CycleReferenceException {
    	new NestedContext(bean, null, true).describe(null);
    }
    /**
     * 返回两个对象之间差异的字段
     * @param left
     * @param right
     */
	public Map<String,DiffNode> different(Object left,Object right){
		return different(left,right,null,null, true, new String[0]);
	}
    /**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 * @param focusName
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			String focusName){
		return different(left,right,Predicates.<String>alwaysTrue(),null, true, focusName);
	}

	/**
     * 返回两个对象之间差异的字段
     * @param left
     * @param right
     * @param focusNames
     */
	public Map<String,DiffNode> different(Object left,Object right,
			String... focusNames){
		return different(left,right,Predicates.<String>alwaysTrue(),null, true, focusNames);
	}
	/**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 * @param focusNames
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			Iterable<String>focusNames){
		return different(left,right,Predicates.<String>alwaysTrue(),null, focusNames, true);
	}
	/**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 * @param originNameFilter
	 * @param focusNames
	 */
	public Map<String,DiffNode> different(Object left,Object right,Predicate<String> originNameFilter,
			Iterable<String>focusNames){
		return different(left,right,originNameFilter,null, focusNames, true);
	}
	/**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 * @param originNameFilter
	 * @param focusNames
	 */
	public Map<String,DiffNode> different(Object left,Object right,Predicate<String> originNameFilter,
			String...focusNames){
		return different(left,right,originNameFilter,null, true,focusNames);
	}
	/**
	 * 返回两个对象之间差异的字段，返回不同的字段差异信息
	 * @see #different(Object, Object, Predicate, Iterable, Iterable, boolean)
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			Predicate<String> originNameFilter,
			String[] forceExcludeNames, 
			boolean includeRequired, 
			String... focusNames){
		List<String> names = null == 
				focusNames ? Collections.<String>emptyList() : Arrays.asList(focusNames); 
		List<String> excludeNames = 
				null == forceExcludeNames ? Collections.<String>emptyList() : Arrays.asList(forceExcludeNames); 
		return different(left,right,originNameFilter,excludeNames, names, includeRequired);
	}
	/**
	 * 返回两个对象之间差异的字段，返回不同的字段差异信息
	 * @param left
	 * @param right
	 * @param originNameFilter
	 * @param forceExcludeNames 强制排除比较的字段名
	 * @param focusNames 比较字段黑/白名单
	 * @param includeRequired 为{@code true}时{@code focusNames}为白名单,否则为黑名单
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			Predicate<String> originNameFilter,
			Iterable<String> forceExcludeNames, Iterable<String> focusNames, 
			boolean includeRequired){
        if(null == left || null == right){
        	HashMap<String,DiffNode> map = new HashMap<>();
        	map.put(".",new DiffNode(left, right));
        	return map;
        }
        /** 用于后续过滤字段的过滤器 */
        Predicate<String> nameFilter = new NestedNameFilter(originNameFilter,forceExcludeNames, focusNames, includeRequired);
        Map<String, DiffNode> diff = new LinkedHashMap<String, DiffNode>();
        different(left, right,diff,nameFilter);
        return diff;
    }
    /**
	 * 在{@code left}对象为基准，输出{@code left VS right}之间的差异到{@code diff}
	 * @param left
	 * @param right
	 * @param diff
	 * @param nameFilter
	 * @see #describe(Object)
	 */
	private void different(Object left,Object right,Map<String,DiffNode> diff, Predicate<String> nameFilter){
    	Map<String, Object> descLeft = describe(left);
    	Map<String, Object> descRight = describe(right);
    	for(Iterator<String> itor = Iterators.concat(descLeft.keySet().iterator(), descRight.keySet().iterator());itor.hasNext();){
    		String nestedName = itor.next();
    		if(!nameFilter.apply(nestedName) || containOrParent(diff.keySet(),nestedName)){
    			/** 名字过滤器不通过或父节点存在则跳过 */
    			continue;
    		}
    		if (nameFilter instanceof NestedNameFilter && !((NestedNameFilter)nameFilter).focusNames.isEmpty()) {
    			/** 指定focusNames时,只比较指定的字段 */
    			addIfNoEqual(diff,nestedName,descLeft.get(nestedName),descRight.get(nestedName));
			}else {
				Object lv = descLeft.get(nestedName);
				Object rv;
				if(descRight.containsKey(nestedName)){
					/** 左右俩侧都有同名的字段 */
					addIfNoEqual(diff,nestedName,lv,descRight.get(nestedName));
				}else if(null != (rv = getProperty(right, nestedName))){
					/** 右侧有值 */
					addIfNoEqual(diff,nestedName,lv,rv);
				}else {
					/** 右侧为null,向上查找右侧存在的父节点作比较 */
					String nk = nestedName;
					while(resolver.hasNested(nk)){
						nk = resolver.removeRight(nk);
						if(descRight.containsKey(nk)){
							break;
						}
					}
					addIfNoEqual(diff,nk,getProperty(left, nk),getProperty(right, nk));
				}
			}
    	}
    }
	/**
	 * 查找集合中是否存在指定的名字或嵌套节点名的父节点
	 * @param names
	 * @param nestName
	 */
	private boolean containOrParent(Set<String>names,String nestName){
		if(names.contains(nestName)){
			return true;
		}
		if(resolver.hasNested(nestName)){
			for(String name:names){
				if(nestName.startsWith(name)){
					return true;
				}
			}
		}
		return false;
	}

	private void addIfNoEqual(Map<String,DiffNode> diff,String name,Object lv,Object rv){
		if(!deepEquals(lv,rv)){
			diff.put(name,new DiffNode(
					differentValue(asAccessibleBean(lv)),
					differentValue(asAccessibleBean(rv))));	
		}
	}

	private Object asAccessibleBean(Object bean){
		if(maybeJsonString(bean) > 0){
			return jsonSupportInstance().parseOrEmptyMap((String)bean,false);
		}
		return bean;
	}

	private Object differentValue(Object input){
    	if(null == input){
    		return "null";
    	}else if(byte[].class.equals(input.getClass())){
    		return "[binary]" + Array.getLength(input) + "bytes";
    	}else if(input.getClass().isArray()){
    		return input.getClass().getComponentType().getSimpleName() + "["+Array.getLength(input)+"]";
    	}else if(input instanceof Iterable){
    		return input.getClass().getSimpleName() + "["+  Iterables.size((Iterable<?>) input) + "] ";
    	} else {
    		return input;
    	}
    }
    /**
	 * 在{@code input}中遍历所有元素找到匹配的值,没找到返回{@code null}
	 * @param input
	 * @param field
	 * @param conditionValue
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	protected Object searchIn(Object input,String field,Object conditionValue) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		if(input  instanceof Iterable) {
			for(Iterator<?> itor = ((Iterable<?>)input).iterator();itor.hasNext();){
				Object element = itor.next();
				if(String.valueOf(getProperty0(element, field)).equals(String.valueOf(conditionValue))){
					return element;
				}
			}
			return null;
		}else if(input instanceof Map){
			/** Map 的所有值中查找*/
			return searchIn(((Map<?, ?>)input).values(),field,conditionValue);
		}else if(null != input && input.getClass().isArray()){
			for(int i=0,end_i=Array.getLength(input);i<end_i;++i){
				Object element = Array.get(input, i);
				if(String.valueOf(getProperty0(element, field)).equals(String.valueOf(conditionValue))){
					return element;
				}
			}
			return null;
		}else if(null == input) {
			throw new NullPointerException("input is null");
		}
		throw new IllegalArgumentException("Property '" + field +
				"' is not search on bean class '" + input.getClass() + "'");
	}
	/**
     * 当{@code nestedBean}为{@code null}时尝试为{@code bean}的{@code name}字段构造新实例
     * @param nestedBean
     * @param bean
     * @param name 字段名
     * @param expectType 期望的新实例类型，为{@code null}使用从字段对应的属性类型
     * @return new instance
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    protected abstract Object tryConstructIfNull(Object nestedBean,Object bean, String name, Class<?> expectType) 
    		throws IllegalAccessException, InvocationTargetException, NoSuchMethodException;
	private static boolean isBaseType(Object object){
		if(null == object){
			return true;
		}
		Class<? extends Object> clazz = object.getClass();
		if(Primitives.unwrap(clazz).isPrimitive()){
			return true;
		}
		if(object instanceof String){
			return true;
		}
		if(object instanceof Date){
			return true;
		}
		if(clazz.isArray()){
			return Primitives.unwrap(clazz.getComponentType()).isPrimitive();
		}
		return false;
	}

	protected static boolean traceEnabled = false;
	public static void setTraceEnabled(boolean traceEnabled) {
		BaseBeanPropertySupport.traceEnabled = traceEnabled;
		MethodUtils.setTraceEnabled(traceEnabled);
	}
	/**
	 * 判断输入参数是否为{@code null}或空<br>
	 * 如果输入参数为@{@link String},{@link Collection},{@link Iterable},{@link Iterator},{@link Map},数组类型则返回其是否为空,
	 * 否则返回{@code false}
	 * @param value 为{@code null}返回{@code true}
	 */
	public static boolean isEmpty(Object value){
		if(null == value){
			return true;
		}else	if(value instanceof String){
			return ((String)value).isEmpty();
		}else if(value instanceof CharSequence){
			return ((CharSequence)value).length() == 0;
		}else if (value instanceof Collection) {
			return ((Collection<?>)value).isEmpty();
		}else if (value instanceof Iterator) {
			return !((Iterator<?>)value).hasNext();
		}else if (value instanceof Iterable) {
			return !((Iterable<?>)value).iterator().hasNext();
		}else if (value instanceof Enumeration) {
			return !((Enumeration<?>)value).hasMoreElements();
		}else if (value instanceof Map) {
			return ((Map<?, ?>)value).isEmpty();
		}else if (value instanceof Multimap) {
			// GUAVA type
			return ((Multimap<?, ?>)value).isEmpty();
		}else if (value instanceof Optional) {
			// GUAVA type
			return isEmpty(((Optional<?>)value).orNull());
		}else if (value.getClass().isArray()) {
			return Array.getLength(value)==0;
		}
		return false;
	}

	/**
	 * 判断输入参数是否都为空或有元素为空<br>
	 * 如果{@code all}为{@code true}判断都为空,即{@code values}中的所有元素都为空时返回{@code true}
	 * 否则判断有空,即 {@code values}中的任一元素为空时就返回{@code true},
	 * {@code values}为空则返回{@code true}
	 * @param all 为{@code true}要求{@code values} 所有元素为空，为{@code false}则只要{@code values}中元素为空即返回{@code true}
	 * @param values 待判空元素迭代器对象
	 * @since 2.8.3
	 * @see #isEmpty(Object)
	 */
	public static boolean isEmpty(boolean all,Iterable<Object> values){
		if(!isEmpty(values)) {
			if(all) {
				for(Iterator<Object> itor=values.iterator();itor.hasNext();) {
					if(!isEmpty(itor.next())) {
						return false;
					}
				}
				return true;
			}else {
				for(Iterator<Object> itor=values.iterator();itor.hasNext();) {
					if(isEmpty(itor.next())) {
						return true;
					}
				}
				return false;
			}
		}
		return true;
	}

	/**
	 * 判断输入参数是否都为空或有元素为空<br>
	 * 如果{@code all}为{@code true}判断都为空,即{@code values}中的所有元素都为空时返回{@code true}
	 * 否则判断有空,即 {@code values}中的任一元素为空时就返回{@code true},
	 * {@code values}为空则返回{@code true}
	 * @param all 为{@code true}要求{@code values} 所有元素为空，为{@code false}则只要{@code values}中元素为空即返回{@code true}
	 * @param values 待判空元素迭代器对象
	 * @since 2.8.3
	 * @see #isEmpty(Object)
	 */
	public static boolean isEmpty(boolean all,Object ...values){
		if(!isEmpty(values)) {
			return isEmpty(all, Arrays.asList(values));
		}
		return true;
	}

	/**
	 * 判断输入参数是否都为空<br>
	 * @param values 待判空元素迭代器对象
	 * @since 2.8.3
	 * @see #isEmpty(boolean, Iterable)
	 */
	public static boolean allEmpty(Iterable<Object> values){
		return isEmpty(true,values);
	}

	/**
	 * 判断输入参数是否都为空<br>
	 * @param values 待判空元素数组
	 * @since 2.8.3
	 * @see #isEmpty(boolean, Object...)
	 */
	public static boolean allEmpty(Object... values){
		return isEmpty(true,values);
	}

	/**
	 * 判断输入参数是否有为空的元素<br>
	 * @param values 待判空元素迭代器对象
	 * @since 2.8.3
	 * @see #isEmpty(boolean, Iterable)
	 */
	public static boolean hasEmpty(Iterable<Object> values){
		return isEmpty(false,values);
	}

	/**
	 * 判断输入参数是否有为空的元素<br>
	 * @param values 待判空元素数组
	 * @since 2.8.3
	 * @see #isEmpty(boolean, Object...)
	 */
	public static boolean hasEmpty(Object... values){
		return isEmpty(false,values);
	}
	/**
	 * 判断对象是否有可能是JSON 字符串,
	 * 如果是String类型且前后以{}包含返回1,
	 * 如果是String类型且前后以[]包含返回2,
	 * 否则返回0
	 * @param object
	 */
	protected static int maybeJsonString(Object object){
		if(object instanceof String){
			String s = ((String)object).trim();
			if(s.length() > 1){
				if(s.charAt(0) == '{' && s.charAt(s.length()-1)=='}'){
					return 1;
				}
				if(s.charAt(0) == '[' && s.charAt(s.length()-1)==']'){
					return 2;
				}
			}
		}
		return 0;
	}
	public static class DiffNode{
		Object left;
		Object right;
		public DiffNode() {
			super();
		}
		public DiffNode(Object left, Object right) {
			this.left = left;
			this.right = right;
		}
		public Object getLeft() {
			return left;
		}
		public void setLeft(Object left) {
			this.left = left;
		}
		public Object getRight() {
			return right;
		}
		public void setRight(Object right) {
			this.right = right;
		}
		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("DiffNode [left=");
			builder.append(left);
			builder.append(", right=");
			builder.append(right);
			builder.append("]");
			return builder.toString();
		}
		
	}
	class NestedContext{
		Object bean;
		/**
		 * 要访问的字段名
		 */
		String name;
		/**
		 * 当{@link #bean}为JSON 字符串时转换的JSON对象
		 */
		Object convertObject;
		/**
		 * 字段值
		 */
		Object value;
		/**
		 * 父节点
		 */
		NestedContext parent;
		/**
		 * 当检测到循环引用时是否抛出{@link CycleReferenceException}
		 */
		boolean throwOnCycleReference;
		private NestedContext(NestedContext parent, String name, Object value) {
			this.parent = parent;
			this.name = name;
			this.value = value;
			this.throwOnCycleReference = parent.throwOnCycleReference;
		}
		private NestedContext(NestedContext parent, Object bean) {
			super();
			this.bean = bean;
			this.parent = parent;
			this.throwOnCycleReference = parent.throwOnCycleReference;
		}
		protected NestedContext(Object bean, String name, boolean throwOnCycleReference) {
			this.bean = bean;
			this.name = name;
			this.throwOnCycleReference = throwOnCycleReference;
		}
		protected NestedContext(Object bean, String name, Object value) {
			this.bean = bean;
			this.name = name;
			this.value = value;
		}
		private void normailzeBean(boolean throwOnfail){
			if(null == convertObject && maybeJsonString(this.bean) > 0){
    			this.convertObject = jsonSupportInstance().parseOrEmptyMap((String)this.bean,throwOnfail);
    		}
		}
		Object getAccessibleBean(){
			return null == convertObject ? bean : convertObject;
		}
		/**
	     * [递归]解析嵌套节点
	     * @param visitor
	     * @throws IllegalAccessException
	     * @throws InvocationTargetException
	     * @throws NoSuchMethodException
	     */
	    Object visit(NestedNodeVisitor visitor)
	    		throws IllegalAccessException, InvocationTargetException,
	    		NoSuchMethodException {
	    	if(visitor.beforeVisit(this)){
	    		return this.bean;
	    	}
	    	
	    	// Resolve nested references
	    	boolean hasNested = resolver.hasNested(this.name);
	    	boolean isIndexed = resolver.isIndexed(this.name);
	    	/**
	    	 * 当为 array[0] 格式的字段索引名时将字段名和字段值分开多进行一次递归
	    	 */
	    	if (hasNested || (isIndexed && this.name.charAt(0) != '[')) {
	    		/** 字段名 */
	    		String propertyName;
	    		NestedContext nextContext;
	    		if(!hasNested && isIndexed && this.name.charAt(0) != '[') {
	    			propertyName = resolver.getProperty(this.name);
	    			/** 索引部分,such as:  [0] */
	    			String idx = this.name.replace(propertyName, "");
	    			nextContext = new NestedContext(this, idx, this.value);
	    		}else {
	    			propertyName =resolver.next(this.name);
	    			nextContext = new NestedContext(this, this.name, this.value);
	    			nextContext.name = resolver.remove(this.name);
				}
	    		normailzeBean(true);
	    		if (resolver.isMapped(propertyName)) {
	    			nextContext.bean = getMappedProperty(getAccessibleBean(), propertyName);
	    		} else if (resolver.isIndexed(propertyName)) {
	    			nextContext.bean = getIndexedProperty(getAccessibleBean(), propertyName);
	    		} else if (resolver.isSearched(propertyName)) {
	    			nextContext.bean = getSearchedProperty(getAccessibleBean(), propertyName);
	    		} else {
	    			nextContext.bean = getSimpleProperty(getAccessibleBean(), propertyName);
	    		}
	    		if(visitor.beofreNext(nextContext)){
	    			return nextContext.bean;
	    		}
	    		// 进入次级节点
	    		Object vret = nextContext.visit(visitor);
	    		if(visitor.afterNext(nextContext)){
	    			return this.bean;
	    		}
	    		return vret;
	    	}
	    	normailzeBean(true);
	    	if(visitor.lastNode(this)){
	    		return this.value;
	    	}
	    	return  null;
	    }
	    
	    /**
	     * 检查子节点是否存在循环引用
	     * @param child
	     * @return 存在循环引用返回{@code true}否则返回{@code false}
	     */
	    private boolean checkCycleRef(Object child) {
	    	for(NestedContext context = this; null != context; context = context.parent) {
	    		if(child == context.bean) {
	    			return true;
	    		}
	    	}
	    	return false;
	    }
	    /**
	     * 计算节点的全路径
	     */
	    private String nestedName() {
			List<String>nestedNames = new ArrayList<>(16);
			for (NestedContext cx = this; null != cx && null != cx.name; cx = cx.parent) {
				nestedNames.add(cx.name);
			}
			Collections.reverse(nestedNames);
			StringBuilder builder = new StringBuilder();
			for(int i=0;i<nestedNames.size();++i) {
				String name = nestedNames.get(i);
				if(i > 0 && !name.startsWith("[")) {
					builder.append('.');
				}
				builder.append(name);
			}
			return builder.toString();
	    }
	    /**
	     * 递归解析所有子节点以平面方式返回所有嵌套节点的值
	     * @param nodes
	     * @return 当前节点的子节点数量
	     */
		int describe(Map<String, Object> nodes){
	    	normailzeBean(false);
	    	List<String> names = getNames();
	    	if(names.isEmpty() && null == parent){
	    		if(null != nodes) {
	    			nodes.put(".", this.bean);
	    		}
	    	}
	    	for(String name : names){
	    		try {
	    			this.name = name;
	    			Object child = readValue();
	    	    	/** 循环引用则抛出异常 */
	    			if(checkCycleRef(child)) {
	    				if(throwOnCycleReference) {
	    					throw new CycleReferenceException(nestedName(),String.valueOf(child));
	    				}else {
	    					/** 叶子节点 */
	    					if(null != nodes) {
	    						nodes.put(nestedName(), "<CycleReferenceException>");
	    					}
	    				}
	    			}else {
	    				NestedContext nestedContext = new NestedContext(this, child);
	    				// 进入次级节点
	    				int cnt = nestedContext.describe(nodes);
	    				if(0 == cnt){
	    					if(null != nodes) {
	    						/** 叶子节点 */
	    						nodes.put(nestedName(), nestedContext.bean);
	    					}
	    				}
	    			}
	    		} finally{
	    			this.name = null;
	    		}
	    	}
	    	return names.size();
	    }
		/**
		 * 返回当前节点对象({@link #bean}或{@link #convertObject})所有的可读取字段名,
		 * 如果对象为{@link Map}返回所有的key,
		 * 对象为数组或{@ List}时使用'[x]'作为元素名,
		 * 对象为{@code null}返回空表
		 */
		@SuppressWarnings("rawtypes")
		private List<String> getNames(){
	    	ArrayList<String> names = new ArrayList<>(16);
	    	Object accessibleBean = getAccessibleBean();
			if(null == accessibleBean) {
	    		// DO NOTHING
	    	}else if(isBaseType(accessibleBean)){
	    		// DO NOTHING
	    	}else if(accessibleBean instanceof Map){
	    		for(Object key:((Map<?, ?>)accessibleBean).keySet()){
	    			if(key instanceof String){
	    				names.add((String)key);
	    			}
	    		}
	    	}else if(accessibleBean instanceof List) {
	    		/** List使用'[x]'作为元素名 */
	    		for(int i=0,elementCount = ((List)accessibleBean).size() ; i < elementCount; ++i) {
	    			names.add( "[" + i + "]" );
	    		}
	    	}else if(accessibleBean.getClass().isArray()) {
	    		/** 数组使用'[x]'作为元素名 */
	    		for(int i=0,elementCount = Array.getLength(accessibleBean) ; i < elementCount; ++i) {
	    			names.add( "[" + i + "]" );
	    		}
	    	}else{
	        	names.addAll(getPropertyNames(bean,3, true));
			}
	    	return names;
	    }
	    Object readValueChecked() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
	    	if(null != name){
	    		normailzeBean(false);
	    		Object accessibleBean = getAccessibleBean();
	    		if(null == accessibleBean){
	    			return null;
	    		}
	    		String next = resolver.hasNested(name) ? resolver.next(name):name;
	    		return getProperty0(accessibleBean,next);
	    		
	    	}else {
	    		// 顶级节点
				return bean;
			}
	    }
	    Object readValue() {
	    	try {
				return readValueChecked();
			} catch (NoSuchMethodException e) {
				return null;
			} catch (ReflectiveOperationException e) {
				throw new RuntimeException(e);
			}
	    }
	}
	/**
	 * 读访问接口实现
	 * @author guyadong
	 *
	 */
	protected class ReadVisitor implements NestedNodeVisitor {

		@Override
		public boolean beforeVisit(NestedContext context) {
			return (context.bean == null || context.name == null);
		}

		@Override
		public boolean beofreNext(NestedContext context) {
			return null == context.bean;
		}

		@Override
		public boolean afterNext(NestedContext context)
				throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
					return false;
		}

		@Override
		public boolean lastNode(NestedContext context) throws 
				InvocationTargetException, NoSuchMethodException, IllegalAccessException {
			context.value = getProperty0(context.getAccessibleBean(),context.name);
			return true;
		}

	}
	/**
	 * 写访问接口实现
	 * @author guyadong
	 *
	 */
	protected class WriteVisitor implements NestedNodeVisitor {
		
		@Override
		public boolean beforeVisit(NestedContext context) {
	        if (context.bean == null) {
	            throw new IllegalArgumentException("No bean specified");
	        }
	        if (context.name == null) {
	            throw new IllegalArgumentException("No name specified for bean class '" +
	            		context.bean.getClass() + "'");
	        }
	        return false;
		}
		
		@Override
		public boolean beofreNext(NestedContext context) 
				throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
			String next = resolver.next(context.parent.name);
			Class<?> expectType = null;
			Object accessibleBean = context.parent.getAccessibleBean();
			if (accessibleBean instanceof Map ) {
                expectType = LinkedHashMap.class;
            	if(resolver.isIndexed(next)){
            		expectType = LinkedList.class;
            		/** next 取字段名作为 tryConstructIfNull 参数 */
            		next=resolver.getProperty(next);
            	}
            }
			context.bean = tryConstructIfNull(context.bean, accessibleBean,next, expectType);
			return false;
		}
		
		@Override
		public boolean afterNext(NestedContext context)
				throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
			if(maybeJsonString(context.bean) > 0){
				/** 当前Bean对象类型为String时,将JSON对象序列化为String保存将由上一级处理 */
				context.bean = jsonSupportInstance().toJSONString(context.convertObject);
				/** 上一级设置下级返回的JSON字符串字段 */
				setProperty0(context.parent.bean,resolver.getProperty(context.parent.name),context.bean);
				return true;
			}else if(maybeJsonString(context.parent.bean) > 0){
				/** 当前Bean对象类型为String时*/
				context.parent.bean = jsonSupportInstance().toJSONString(context.parent.convertObject);
				return true;
			}
			return false;
		}

		@Override
		public boolean lastNode(NestedContext context) throws 
				InvocationTargetException, NoSuchMethodException, IllegalAccessException {
			
			if(context.checkCycleRef(context.value)) {
				throw new CycleReferenceException(context.name,String.valueOf(context.value));
			}
			
			setProperty0(context.getAccessibleBean(),context.name,context.value);
			return false;
		}
		
	}
	static class NestedNameFilter implements Predicate<String>{
		/** 定义需要比较的字段名 */
		final Set<String> focusNames;
		final Predicate<String> nameFilter;
		final Set<String> forceExcludeNames;
		final boolean includeRequired;
		private Set<String> normalize(Iterable<String> input){
			if(null != input ){
				/** 过滤出非null的字段名集 */
				Iterable<String> filtered = Iterables.filter(input,Predicates.notNull());
				/** 统一转成驼峰(camel-case)命名格式,以方便后续比较 */
				Iterable<String> cameCaseNames = Iterables.transform(filtered, TO_NESTED_CAMEL_CASE);
				return Sets.newHashSet(cameCaseNames);
			}else {
				return Collections.emptySet();
			}
		}
		/**
		 * @param originNameFilter
		 * @param forceExcludeNames 强制排除比较的字段名
		 * @param focusNames 比较字段黑/白名单
		 * @param includeRequired 为{@code true}时{@code focusNames}为白名单,否则为黑名单
		 */
		NestedNameFilter(Predicate<String> originNameFilter,
				Iterable<String> forceExcludeNames, 
				Iterable<String> focusNames, 
				boolean includeRequired) {
			this.nameFilter = MoreObjects.firstNonNull(originNameFilter, Predicates.<String>alwaysTrue());
			this.focusNames= normalize(focusNames);
			this.forceExcludeNames= normalize(forceExcludeNames);
			this.includeRequired = includeRequired;
		}
		@Override
		public boolean apply(String input) {
			boolean allowed = false;
			if(null != input){
				/** 自动实现 snake-case 到 camel-case 的转换*/
				input = toNestedCamelcase(input);
				/** 强制排除的字段不参与比较 */
				if(forceExcludeNames.contains(input)){
					return false;
				}
				if(focusNames.isEmpty()){
					/** 为空时所有字段都在比较范围之内 */
					allowed = true;
				}else if(focusNames.contains(input)){
					allowed = true;
				}else {
					for(String name : focusNames){
						if(input.length() > name.length()){
							if(input.startsWith(name)){
								allowed = true;
								break;
							}
						} else if(name.length() > input.length()){
							if(name.startsWith(input)){
								/** 允许遍历父节点  */
								allowed = true;
								break;
							}
						}else if(name.equals(input)){
							/** DEAD CODE,不会走到这里 */
							allowed = true;
							break;
						}
					}
				}
			}
			if(includeRequired){
				return allowed ? nameFilter.apply(input) : false;
			}else {
				return allowed ? !nameFilter.apply(input) : false;
			}
		}
		
	}
}
